#!/usr/bin/python3

import os
import re
import time
import requests
from requests.auth import HTTPBasicAuth
from lxml import etree
from src.libs.logger import logger
from src.config import constant, global_config
from src.libs.base import http
from src.libs.csvrw import CSVRW
from src.libs.common_commands import Command
from src.community.openeuler_community_info_query import sig_info_query


EXCLUDED_RPM_INFO = ["debugsource", "debuginfo", "src.rpm", "debug"]
TARGET_RPM_INFO = ["src.rpm"]
# EXCLUDED_RPM_INFO = ["debugsource", "debuginfo", "src.rpm", "debug"]
BAD_STATUS = ['failed', 'unresolvable', 'broken']
VALID_ARCHES = ["x86_64", "aarch64", "loongarch64"]

class OpenSuseOBSAPI:
    """
    opensuse obs中的相关接口操作
    """

    def __init__(self, account="senlin", password="obs***xsl") -> None:
        # 获取环境变量的值
        self._account = os.getenv('OBS_ACCOUNT', account)
        self._password = os.getenv('OBS_PASS', password)


    def _url(self, url, **kwargs):
        return constant.OBS_HOST + url.format(**kwargs)

    def _meta(self, package, project):
        meta = """<package name="{package}" project="{project}">
                <title />
                <description />
              </package>""".format(
            package=package, project=project
        )
        return meta

    @property
    def _auth(self):
        return HTTPBasicAuth(self._account, self._password)

    def get_source_package(self, projects, branch='master', save = True):
        """
        @description :获取指定project的package列表
        ----------- 
        @param :

        -----------
        @returns :
        -----------
        """
        package_list = []
        res_data = []

        if not projects:
            projects = constant.GITEE_BRANCH_PROJECT_MAPPING[branch] # Get the mapped project list

        if not projects:
            logger.warning("No projects to be queryed")
            return res_data

        result_csv_name ='_'.join([projects[0].replace(':', '_'), 'source', '.csv'])
        for pj in projects:
            url = self._url(url="source/{pj}", pj=pj)
            response = http.get(url, auth=self._auth)
            if response.status_code != 200:
                logger.error(f"{response}")
                continue

            etree_element = etree.HTML(response.text)
            try:
                all_entry = etree_element.xpath("//entry")
                for entry in all_entry:
                    name = entry.xpath("./@name")[0]
                    package_list.append(name) # get name
                    __, spec_name, srpm_name = sig_info_query.get_gitee_repo_related_name('gitee_repo', name)
                    
                    src_openeuler_name = 'src-openeuler/%s' % name
                    res_data.append([src_openeuler_name, spec_name, srpm_name, pj])
            except (TypeError, IndexError) as err:
                logger.warning("analyse {url} failed: {err}")
                continue
                
        if save:
            csv_title = ['Source Package', 'specname', 'srpm_name', 'Project']
            CSVRW.save_by_row(result_csv_name, res_data, csv_title, 'w+')
            project_record_path = global_config.OE_PACKAGE_PROBLEMS_RECORD_PATH.format(oe_version=branch)
            upload_files = [result_csv_name]
            # Command().osc_remove(project_record_path, upload_files)

        return package_list

    def get_prj_package_detail(self, project, package, arch):
        """
        
        Args:
        
        Returns:
        
        """

        url = self._url(url="build/{project}/_result?view=status&package={package}&arch={arch}&repository=standard_{arch}", \
            project=project, package=package, arch=arch)
        response = http.get(url, auth=self._auth)

        if response.status_code != 200:
            logger.error(f"response.status_code: {response.status_code}")
            return

        etree_element = etree.HTML(response.text)
        all_result = etree_element.xpath("//result") # get all the 'result'
        detail = all_result[0].xpath("./status")[0].xpath("./details/text()")
        logger.info(detail)
        return detail

    @staticmethod
    def parse_build_result(project, etree_element, unresolvable_info, res_sig_names, need_status):
        """
        prase the build result,especcially the details of bad packages

        Args:
            project: project name
            etree_element: root node of project's _result html

        Returns:
            _bad_details: record of detail of bad packages

        """

        bad_details = dict() # record detail of package, which status is bad
        all_result = etree_element.xpath("//result") # get all the 'result'
        pkgs = set()

        num = 0
        for arch_result in all_result:
            arch = arch_result.xpath("./@arch")[0]
            logger.info(f"current arch is {arch}")
            arch_pkg_status = '%s_status' % (arch)
            arch_pkg_detail = '%s_detail' % (arch)

            # get all 'status' nodes of current arch
            for status in arch_result.xpath("./status"):
                package = status.xpath("./@package")[0] # get package name
                code = status.xpath("./@code")[0]
                if code not in need_status:
                    continue

                pkg_detail = ''
                if code == 'unresolvable':
                    details_text = status.xpath("./details/text()")
                    if details_text[0] == 'succeeded':
                        continue
                    details = details_text[0].split(', ')
                    # 获取unresolvable的详细信息
                    for nothing_provide in details:
                        nothing_provide = nothing_provide.strip()
                        if not nothing_provide in unresolvable_info.keys():
                            unresolvable_info[nothing_provide] = set()
                        unresolvable_info[nothing_provide].add(package)
                    pkg_detail = 'please reference unresolvable info sheet'
                elif code == 'broken':
                    pkg_detail = 'please contact CICD sig: small_leek'
                elif code == 'failed':
                    live_log_url = constant.OBS_PROJECT_LIVE_LOG.format(
                        obs_project = project,
                        package_repo = package,
                        arch = arch)
                    pkg_detail = live_log_url
                else:
                    pkg_detail = 'normal'
                
                if package not in pkgs:
                    bad_details[package] = dict()
                    bad_details[package]['project'] = project
                    package_owner_info =  sig_info_query.get_package_details(package)
                    bad_details[package].update(package_owner_info)
                    if bad_details[package]['sig'] in ['Private', 'sig-recycle']:
                        bad_details[package]['gitee_status'] = bad_details[package]['sig']
                    else:
                        bad_details[package]['gitee_status'] = 'normal'
                bad_details[package][arch_pkg_detail] = pkg_detail
                bad_details[package][arch_pkg_status] = code
                res_sig_names.add(package_owner_info['sig'])
                pkgs.add(package)
                num += 1
                logger.info(f"catch {num}: {package} {code}  {arch}")

        return pkgs, bad_details

    def create_package(self, project, package):
        """
        创建软件包
        """

        url = self._url(
            url="source/{project}/{package}/_meta", project=project, package=package
        )
        meta = self._meta(package, project).encode("utf-8")
        response = requests.put(
            url,
            auth=self._auth,
            data=meta,
            timeout=15,
        )
        return response.status_code == 200

    def exists_package(self, project, package):
        """
        判断指定工程下是否存在特定的软件包
        """

        url = self._url(
            url="source/{project}/{package}", project=project, package=package
        )
        response = http.get(url, auth=self._auth)
        if response.status_code == 404:
            return False
        if response.status_code == 200 and package in response.text:
            return True


    def trigger_build(self, projects=None, branch=None, package=None, code=None, arch=None):
        """
        @description :触发单个软件包或整个工程的编译
        -----------
        @param :
        -----------
        @returns :
        -----------
        """

        if branch is not None:
            projects = constant.GITEE_BRANCH_PROJECT_MAPPING[branch] # Get the mapped project list
            logger.info(projects)

        if not projects:
            logger.warning("no project parameter")
            return False
        
        for pj in projects:
            url = self._url(url="build/{project}?cmd=rebuild", project=pj)
            data = dict()
            if package:
                data["package"] = package
            if code:
                data["code"] =code
            if arch:
                data["arch"] = arch

            response = requests.post(url, auth=self._auth, data=data)
            logger.info(response)

            if response.status_code == 200:
                logger.info(f'build project: {pj}, code: {code} sucessfully!')
        return

    def get_package_binary_list(self, project, package, repository="standard_aarch64", arch="aarch64"):
        """
        查询project/package的当前已published的binary列表
        Args:
            project:
            package:
            repository:
            arch:
        Returns:
            package_bin_list
        """

        package_bin_list = []

        url = self._url(
            url="build/{project}/{repository}/{arch}/{package}",
            project=project,
            package=package,
            repository=repository,
            arch=arch,
        )

        response = http.get(url, auth=self._auth)
        if response.status_code != 200:
            logger.error(f"{response}")
            return None

        etree_element = etree.HTML(response.text)
        binary_names = etree_element.xpath("//binary/@filename")
        for file in binary_names:
            if file.endswith(".rpm"):
                binary_name = file.rsplit('-', 2)[0]
                # logger.info(f"{src}: {binary_name}")
                if file.endswith(".src.rpm"):
                    package_bin_list.append([package, binary_name, "src", project])
                else:
                    package_bin_list.append([package, binary_name, "bin", project])

        return package_bin_list